/**
 * 
 */
package dh.abs.info;

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import dh.abs.Agent;
import dh.abs.SuspendException;
import dh.abs.Suspendable;
import dh.abs.util.SimulationRandom;

/**
 * This class represents an agent of this information diffusion simulation.
 * This agent is specified by only id field.
 * @author denialhumanity
 *
 */
public class InformationDiffusionAgent implements Agent, Suspendable {

	/** environment instance that has this agent */
	private InformationDiffusionEnvironment env;
	
	/** unique id of this agent */
	private String id;
	
	/** information capacity */
	private Integer informationCapacity;
	
	/** time-ordered information list this agent has. */
	private List<Information> infoList;
	
	/**
	 * Default constructor.
	 * This is not used.
	 */
	@SuppressWarnings("unused")
	private InformationDiffusionAgent() {
	}
	
	public InformationDiffusionAgent(InformationDiffusionEnvironment env, String id, Integer infoCapacity) {
		this.env = env;
		this.id = id;
		this.informationCapacity = infoCapacity;
		this.infoList = new ArrayList<Information>();
	}
	
	/**
	 * Constructor of this class.
	 * This constructor creates an instance of this class using environment and resumeData.
	 * This is used for loading suspended data.
	 * @param env 
	 * @param resumedData
	 */
	public InformationDiffusionAgent(InformationDiffusionEnvironment env, File resumedData) {
		this.env = env;
		this.id = resumedData.getName().substring(this.getClass().getSimpleName().length());
	}
	
	/**
	 * Some agent transfers one information to this agent.
	 * @param info
	 */
	public void addInformation(Information info) {
		if(infoList.size() != informationCapacity) {
			infoList.add(info);
		} else {
			for(int i = 0; i < infoList.size(); i++) {
				if(!infoList.get(i).equals(info)) {
					infoList.remove(i);
					infoList.add(info);
					break;
				}
			}
		}
	}
	
	/**
	 * List of information is added to this agent.
	 * @param infoCollection
	 */
	public void addInformationCollection(Collection<Information> infoCollection) {
		for(Information info: infoCollection) {
			addInformation(info);
		}
	}
	
	/* (non-Javadoc)
	 * @see dh.abs.Suspendable#storeToFile(java.io.File)
	 */
	public void storeToFile(File file) throws SuspendException {
		for(Information info: infoList) {
			info.storeToFile(file);
		}
	}
	
	/* (non-Javadoc)
	 * @see dh.abs.Agent#preStep()
	 */
	public void preStep() {

	}

	/* (non-Javadoc)
	 * @see dh.abs.Agent#nextStep()
	 */
	public void nextStep() {
		List<InformationDiffusionAgent> list = env.getAgentList();
		InformationDiffusionAgent target = null;
		do {
			target = list.get(SimulationRandom.getInstance().getRand().nextInt(list.size()));
		} while(this.equals(target));
		
		Information diffusionInfo = infoList.get(SimulationRandom.getInstance().getRand().nextInt(infoList.size()));
		double probability = (double)informationCapacity/target.informationCapacity;
		if(SimulationRandom.getInstance().getRand().nextDouble() < probability) { 
			diffusionInfo.transfer(target);
		}
	}

	/* (non-Javadoc)
	 * @see dh.abs.Agent#postStep()
	 */
	public void postStep() {

	}

	/* (non-Javadoc)
	 * @see dh.abs.Suspendable#getID()
	 */
	public String getID() {
		return id;
	}
	
	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		StringBuffer sb = new StringBuffer();
		sb.append(env.getCurrentStep() + "(" + getSelection() +"):");
		
		for(Information info: infoList) {
			sb.append(info.getID() + " ");
		}
		
		return sb.toString();
	}
	
	/**
	 * This method return the ID of one information that this agent selects.
	 * "Select" means, typically, the information this agent has the most.
	 * @return
	 */
	public String getSelection() {
		Map<String, Integer> selectedMap = new HashMap<String, Integer>();
		
		Comparator<Entry<String, Integer>> c = new Comparator<Map.Entry<String,Integer>>() {
			public int compare(Entry<String, Integer> o1, Entry<String, Integer> o2) {
				return o2.getValue().compareTo(o1.getValue());
			}
		};
		for(Information info: infoList) {
			int num = 0;
			if(selectedMap.containsKey(info.getID())) {
				num = selectedMap.remove(info.getID());
			}
			selectedMap.put(info.getID(), num+1);
		}
		if(selectedMap.size() > 0) {
			List<Entry<String,Integer>> selectedList = new ArrayList<Entry<String,Integer>>();
			selectedList.addAll(selectedMap.entrySet());
			Collections.sort(selectedList, c);
			return selectedList.get(0).getKey();
		}
		return "null";
	}

	/**
	 * @return the infoList
	 */
	public List<Information> getInfoList() {
		return infoList;
	}

	/* (non-Javadoc)
	 * @see dh.abs.Suspendable#loadFromFile(java.io.File)
	 */
	public void loadFromFile(File file) throws SuspendException {
		//Nothing to do
	}

	/**
	 * This method is used for loading information data.
	 * Usually, information capacity is not initialized, so it is extended.
	 * @param info
	 */
	public void extendInformation(Information info) {
		infoList.add(info);
		informationCapacity = infoList.size();
	}

}
